/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package saltocaballo.expertos;

import java.awt.Component;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import saltocaballo.comunes.logger;
import saltocaballo.entidades.Casilla;
import saltocaballo.entidades.Tablero;

/**
 *
 * @author Martinoxs
 */
public class arcoConsistencia implements Busqueda{

    movimientosCaballo movimientos;
    Tablero tablero;
    logger log;
    
    public arcoConsistencia(){
        movimientos = new movimientosCaballo();
    }
    
    @Override
    public String resolver(Tablero tablero, String metodo, logger logg) {
        
        this.tablero = tablero;
        String rdo = "";
        boolean sinSolucion = false;
        boolean consistente = false;
        log = logg;
        int iteraciones = 0;
        
        iniciarLog(metodo);
        
        String casillaElegida = tablero.getCasillaInicio().getCoordenada();
        String casillaAnterior = "Ninguna";
        String casillaProx = "Ninguna";
        tablero.ocuparCasilla(casillaElegida);
        tablero.setTieneCaballo(casillaElegida, true);
        
        Map<String, List<String>> movsCasillas = new HashMap<String, List<String>>();
        Map<String, List<String>> movsProhibidos = new HashMap<String, List<String>>();
        List<String> listaMovs;
        List<String> listaRdo = new ArrayList<String>();
        
        movsCasillas = recargarMovimientos(movsCasillas, metodo);       
        
        while(!tablero.tableroResuelto(tablero.getCopiaPanel()) && !sinSolucion){
            
            if(casillaElegida.equals("Ninguna") && casillaAnterior.equals("Ninguna")) {
                sinSolucion = true;
            }
            
            log.escribirLinea("-------------------------------------------------");
            log.escribirLinea("Iteracion: " + iteraciones + "  - CasillaActual: " + casillaElegida + " - CasillaAnterior: " + casillaAnterior + "    - CasillaProx: " + casillaProx);
            if(movsCasillas.get(casillaElegida).isEmpty()){
                log.escribirLinea("    - Movimientos(" + casillaElegida + "): vacio");
            } else {
                for(String s: movsCasillas.get(casillaElegida)){
                    log.escribirLinea("    - Movimientos(" + casillaElegida + "): " + s);
                }
            }
            
            log.escribirLinea("");
            
            if(listaRdo.isEmpty()){
                log.escribirLinea("    - MovimientosRdo: vacio");
            } else {
                for(String s: listaRdo){
                    log.escribirLinea("    - MovimientosRdo: " + s);
                }
            }
            log.escribirLinea("");
            log.escribirLinea("");
            
            for(Map.Entry<String,List<String>> mov: movsProhibidos.entrySet()){
                for(String m: mov.getValue()){
                    log.escribirLinea("    - MovimientosProhibidos(" + mov.getKey() + "): " + m);
                }
            }
            
            iteraciones++;
            
            casillaAnterior = casillaElegida;
            tablero.setTieneCaballo(casillaAnterior, false);
            
            if (!movsCasillas.get(casillaElegida).isEmpty()){
                casillaElegida = movsCasillas.get(casillaElegida).get(0);
            } else {
                sinSolucion = true;
                break;
            }
            tablero.ocuparCasilla(casillaElegida);
            tablero.setTieneCaballo(casillaElegida, true);
        
            
            log.escribirLinea("Elijoccasilla: " + casillaElegida + " <-- Ocupar");
            log.escribirLinea("CasillaAnterior: " + casillaAnterior);
            listaRdo.add(casillaAnterior);
            log.escribirLinea("    Agrego a MovimientosRdo: " + casillaAnterior);
            
            if(!movsCasillas.get(casillaElegida).isEmpty()){
                listaMovs = movsCasillas.get(casillaElegida);
                log.escribirLinea("Cargo movimientoss para: " + casillaElegida);
                for(String s: listaMovs){
                    log.escribirLinea("    * Mov: " + s);
                }
                
                movsCasillas = recargarMovimientos(movsCasillas, metodo);
            } else {
                log.escribirLinea("    * Casilla " + casillaElegida + " sin movimientos, volver atras");
                listaRdo.remove(listaRdo.size() - 1);
                log.escribirLinea("    * Remuevo " + casillaElegida + " de MovimientosRdo");
                
                movsCasillas.get(casillaAnterior).remove(movsCasillas.get(casillaAnterior).indexOf(casillaElegida));
                log.escribirLinea("    * Remuevo " + casillaElegida + " de los movimientos de " + casillaAnterior);
                
                boolean hayMovs = false;
                while(!hayMovs){
                    tablero.desocuparCasilla(casillaElegida, tablero.getCopiaPanel());
                    tablero.setTieneCaballo(casillaElegida, false);
                    log.escribirLinea("    * Desocupar casillaElegida " + casillaElegida);
                    casillaElegida = casillaAnterior;
                    log.escribirLinea("    * Nueva casillaElegida: " + casillaElegida);
                    
                    if(!listaRdo.isEmpty()){
                        casillaAnterior = listaRdo.get(listaRdo.size()-1);
                    } else {
                        casillaAnterior = "Ninguna";
                    }
                    
                    log.escribirLinea("    * Nueva casillaAnterior: " + casillaAnterior);
                    
                    if(!movsCasillas.get(casillaElegida).isEmpty()){
                        hayMovs = true;
                        log.escribirLinea("    * Casilla con movimientos encontrada: " + casillaElegida);
                    } else {
                        log.escribirLinea("    * Casilla " + casillaElegida + " sin movimientos, volver atras");
                        casillaAnterior = listaRdo.get(listaRdo.size() - 1);
                        listaRdo.remove(listaRdo.size() - 1);
                    }
                }
            }
            
            if (movsCasillas.get(casillaElegida).size() == 1){
                String esCasillaFinal = movsCasillas.get(casillaElegida).get(0);
                tablero.ocuparCasilla(esCasillaFinal);
                tablero.setTieneCaballo(esCasillaFinal, true);
                listaRdo.add(casillaElegida);
                listaRdo.add(esCasillaFinal);
                if(!tablero.tableroResuelto(tablero.getCopiaPanel())){
                    tablero.desocuparCasilla(esCasillaFinal, tablero.getCopiaPanel());
                    tablero.setTieneCaballo(esCasillaFinal, false);
                    listaRdo.remove(listaRdo.indexOf(casillaElegida));
                    listaRdo.remove(listaRdo.indexOf(esCasillaFinal));
                }
            }
            
            if(!tablero.tableroResuelto(tablero.getCopiaPanel())){
                if(!tableroConsistente()){
                    casillaProx = casillaElegida;
                    tablero.setTieneCaballo(casillaProx, false);
                    tablero.desocuparCasilla(casillaProx, tablero.getCopiaPanel());
                    log.escribirLinea("    * Desocupo: " + casillaProx + ", volver atras");
                    
                    log.escribirLinea("    *Agrego: " + casillaProx + ", a MovimientosProhibidos de: " + casillaAnterior);
                    agregarCasProhibida(casillaAnterior, casillaProx, movsProhibidos);
                    
                    movsCasillas = recargarMovimientos(movsCasillas, metodo);
                    
                    casillaElegida = casillaAnterior;
                    casillaAnterior = listaRdo.get(listaRdo.size() - 1);
                    
                    if (!listaRdo.isEmpty()){
                        listaRdo.remove(listaRdo.size() - 1);
                    }
                    
                    if (movsCasillas.get(casillaElegida).contains(casillaProx)){
                        movsCasillas.get(casillaElegida).remove(movsCasillas.get(casillaElegida).indexOf(casillaProx));
                        log.escribirLinea("    * Remuevo " + casillaProx + ", de los movimientos de " + casillaElegida);
                    }
                    
                    List<String> casillasProhibida = movsProhibidos.get(casillaElegida);
                    for(String casillaP: casillasProhibida){
                        if (movsCasillas.get(casillaElegida).contains(casillaP)){
                            movsCasillas.get(casillaElegida).remove(movsCasillas.get(casillaElegida).indexOf(casillaP));
                            log.escribirLinea("    * Remuevo " + casillaP + ", de los movimientos de " + casillaElegida);
                        }
                    }
                    
                    if(!movsCasillas.get(casillaElegida).isEmpty()){
                        tablero.setTieneCaballo(casillaElegida, false);
                        casillaElegida = movsCasillas.get(casillaElegida).get(0);
                        log.escribirLinea("    * Nueva casillaElegida: " + casillaElegida + " <-- ocupar");
                        log.escribirLinea("    * Nueva casillaAnterior: " + casillaAnterior);
                        tablero.ocuparCasilla(casillaElegida);
                        movsCasillas = recargarMovimientos(movsCasillas, metodo);
                        listaRdo.add(casillaAnterior);
                        
                        if(movsCasillas.get(casillaElegida).isEmpty()){
                            log.escribirLinea("    * Casilla " + casillaElegida + " sin movimientos, volver atras");
                            tablero.desocuparCasilla(casillaElegida, tablero.getCopiaPanel());
                            log.escribirLinea("    * Desocupo casilla " + casillaElegida);
                            listaRdo.remove(listaRdo.size() - 1);
                            log.escribirLinea("    * Remuevo " + casillaAnterior + " de MovimientosRdo");

                            boolean hayMovs = false;
                            while(!hayMovs){
                                casillaElegida = casillaAnterior;
                                casillaAnterior = listaRdo.get(listaRdo.size()-1);
                                if(!movsCasillas.get(casillaElegida).isEmpty()){
                                    log.escribirLinea("    * Casilla con movimientos encontrada: " + casillaElegida);
                                    if (movsProhibidos.containsKey(casillaElegida)){
                                        casillasProhibida = movsProhibidos.get(casillaElegida);
                                        for(String casillaP: casillasProhibida){
                                            if (movsCasillas.get(casillaElegida).contains(casillaP)){
                                                movsCasillas.get(casillaElegida).remove(movsCasillas.get(casillaElegida).indexOf(casillaP));
                                                log.escribirLinea("    * Remuevo " + casillaP + ", de los movimientos de " + casillaElegida);
                                            }
                                        }
                                    }
                                    if (!movsCasillas.get(casillaElegida).isEmpty()){
                                        hayMovs = true;
                                    }
                                } else {
                                    casillaAnterior = listaRdo.get(0);
                                    listaRdo.remove(listaRdo.size() - 1);
                                }
                            }
                        }
                        
                        if(!tableroConsistente()){
                            log.escribirLinea("    $ Desocupo casilla: " + casillaElegida);
                            tablero.desocuparCasilla(casillaElegida, tablero.getCopiaPanel());
                            agregarCasProhibida(casillaAnterior, casillaElegida, movsProhibidos);
                            log.escribirLinea("    $ Agrego " + casillaElegida + ", a MovimientosProhibidos de " + casillaAnterior);
                            casillaElegida = casillaAnterior;
                            log.escribirLinea("    $ casillaNueva: " + casillaElegida);
                            listaRdo.remove(listaRdo.size()-1);
                            log.escribirLinea("    $ Remuevo " + casillaAnterior + " de MovimientosRdo");
                            if (!listaRdo.isEmpty()) {
                                casillaAnterior = listaRdo.get(listaRdo.size()-1);
                            } else {
                                casillaAnterior = "Ninguna";
                            }
                            log.escribirLinea("    $ casillaAnterior: " + casillaAnterior);
                            
                            if (movsProhibidos.containsKey(casillaElegida)){
                                casillasProhibida = movsProhibidos.get(casillaElegida);
                                for(String casillaP: casillasProhibida){
                                    if (movsCasillas.get(casillaElegida).contains(casillaP)){
                                        movsCasillas.get(casillaElegida).remove(movsCasillas.get(casillaElegida).indexOf(casillaP));
                                        log.escribirLinea("    $ Remuevo " + casillaP + ", de los movimientos de " + casillaElegida);
                                    }
                                }
                            }
                            
                            if (movsCasillas.get(casillaElegida).isEmpty()){
                                log.escribirLinea("    $ Sin movimientos para " + casillaElegida + ", volver atras");
                                agregarCasProhibida(casillaAnterior, casillaElegida, movsProhibidos);
                                log.escribirLinea("    $ Agrego " + casillaElegida + ", a movsProhibidos de: " + casillaAnterior);
                                
                                boolean hayMovs = false;
                                while(!hayMovs){
                                    tablero.desocuparCasilla(casillaElegida, tablero.getCopiaPanel());
                                    log.escribirLinea("    $ Desocupo " + casillaElegida);
                                    casillaElegida = casillaAnterior;
                                    log.escribirLinea("    $ CasillaNueva: " + casillaElegida);
                                    if (!listaRdo.isEmpty()){
                                        listaRdo.remove(listaRdo.size()-1);
                                        if(!listaRdo.isEmpty()){
                                            casillaAnterior = listaRdo.get(listaRdo.size()-1);
                                        } else {
                                            casillaAnterior = "Ninguna";
                                        }
                                    }
                                    log.escribirLinea("    $ CasillaAnterior: " + casillaAnterior);
                                    movsCasillas = recargarMovimientos(movsCasillas, metodo);
                                    
                                    if(casillaElegida.equals("Ninguna") && casillaAnterior.equals("Ninguna")) break;
                                    
                                    if(!movsCasillas.get(casillaElegida).isEmpty()){
                                        log.escribirLinea("    $ Casilla con movimientos: " + casillaElegida);
                                        if (movsProhibidos.containsKey(casillaElegida)){
                                            casillasProhibida = movsProhibidos.get(casillaElegida);
                                            for(String casillaP: casillasProhibida){
                                                if (movsCasillas.get(casillaElegida).contains(casillaP)){
                                                    movsCasillas.get(casillaElegida).remove(movsCasillas.get(casillaElegida).indexOf(casillaP));
                                                    log.escribirLinea("       $ Remuevo " + casillaP + ", de los movimientos de " + casillaElegida);
                                                }
                                            }
                                        }
                                        if (!movsCasillas.get(casillaElegida).isEmpty()){
                                            hayMovs = true;
                                            tablero.ocuparCasilla(casillaElegida);
                                            log.escribirLinea("    $ Ocupar casilla " + casillaElegida);
                                        }
                                    } else {
                                        log.escribirLinea("        $ Sin movimientos para " + casillaElegida + ", volver atras");
                                        tablero.desocuparCasilla(casillaElegida, tablero.getCopiaPanel());
                                        log.escribirLinea("        $ Desocupar casillaElegida " + casillaElegida);
                                        casillaElegida = casillaAnterior;
                                    }
                                }
                            }
                        }
                        
                    } else {
                        consistente = false;
                        while(!consistente){
                            log.escribirLinea("    * Sin movimientos para: " + casillaElegida + "  -   casillaProx: " + casillaProx + "    -   casillaAnt: " + casillaAnterior);
                            
                            tablero.desocuparCasilla(casillaElegida, tablero.getCopiaPanel());
                            tablero.setTieneCaballo(casillaElegida, false);
                            log.escribirLinea("        * Desocupar: " + casillaElegida);
                            casillaProx = casillaElegida;
                            
                            if(!listaRdo.isEmpty()){
                                casillaElegida = listaRdo.get(listaRdo.size() - 1);
                                log.escribirLinea("        * casillaNueva: " + casillaElegida);

                                agregarCasProhibida(casillaElegida, casillaProx, movsProhibidos);
                                                        
                                if (!listaRdo.isEmpty())listaRdo.remove(listaRdo.size() - 1);
                                if(!listaRdo.isEmpty()){
                                    casillaAnterior = listaRdo.get(listaRdo.size() - 1);
                                    log.escribirLinea("        * casillaAnterior: " + casillaAnterior);
                                } else {
                                    casillaAnterior = "Ninguna";
                                }
                                log.escribirLinea("        * Agrego: " + casillaProx + ", a MovimientosProhibidos de: " + casillaElegida);

                                movsCasillas = recargarMovimientos(movsCasillas, metodo);

                                for(String casillaProhibida: movsProhibidos.get(casillaElegida)){
                                    if(movsCasillas.get(casillaElegida).contains(casillaProhibida)){
                                        log.escribirLinea("        * Remover: " + casillaProhibida);
                                        movsCasillas.get(casillaElegida).remove(movsCasillas.get(casillaElegida).indexOf(casillaProhibida));
                                    }    
                                }

                                if(!movsCasillas.get(casillaElegida).isEmpty()){
                                    log.escribirLinea("        * casilla con movimientos encontrada: " + casillaElegida);
                                    consistente = true;
                                    movsProhibidos.remove(casillaProx);
                                    log.escribirLinea("        * Elimino " + casillaProx + " de MovimientosProhibidos");
                                } else {
                                    movsProhibidos.remove(casillaProx);
                                    log.escribirLinea("        * Elimino " + casillaProx + " de MovimientosProhibidos");
                                } 
                            } else {
                                sinSolucion = true;
                                break;
                            }
                        }
                    }
                }
            }
        }
                
        if(sinSolucion){
            rdo = "No se encontró solución al tablero.";
        } else {
            rdo = "Tablero Resuelto en: " + iteraciones + " iteraciones.";
        }
        
        if (listaRdo.contains(tablero.getCasillaInicio().getCoordenada())){
            listaRdo.remove(0);
        }
        
        log.escribirLinea("---------------------------------------------");
        log.escribirLinea("---------------------------------------------");
        log.escribirLinea("TERMINADO: ");
        
        int i = 0;
        for (String c: listaRdo){
            i++;
            rdo += "\nMovimiento("+i+"): " + c;
            log.escribirLinea("    MovimientoRDO: " + c);
        } 
        return rdo;
    }

    private Map<String,List<String>> recargarMovimientos(Map<String, List<String>> movsCasillas, String metodo) {
        
        movsCasillas.clear();
        List<String> listaMovs;        
        
        movsCasillas.clear();
        
        //Cargo el Map con casillas y movimientos
        for(Component casilla: tablero.getCopiaPanel().getComponents()){
            if(casilla.getClass().equals(Casilla.class)){
                Casilla cas = (Casilla) casilla;
                listaMovs = movimientos.ordenarMovimientos(movimientos.getMovimientosValidos(cas.getCoordenada(), tablero, tablero.getCopiaPanel()), tablero, tablero.getCopiaPanel(), metodo);
                movsCasillas.put(cas.getCoordenada(), listaMovs);
            }
        }
        
        return movsCasillas;
    }

    private boolean tableroConsistente() {
        boolean rdo = true;
        
        for(Component c: tablero.getCopiaPanel().getComponents()){
            if(c.getClass().equals(Casilla.class)){
                Casilla casilla = (Casilla) c;
                if (!casilla.fueVisitada() && !casilla.tieneCaballo() && !casilla.tieneObstaculo()){
                    if(movimientos.getMovimientosValidos(casilla.getCoordenada(), tablero, tablero.getCopiaPanel()).isEmpty()){
                        log.escribirLinea("    * Tablero inconsistente, casilla sin movimientos: " + casilla.getCoordenada());
                        rdo = false;
                    }
                }
            }
        }
        return rdo;
    }

    private void agregarCasProhibida(String casillaAnterior, String casillaProx, Map<String,List<String>> movsProhibidos) {
        if(movsProhibidos.containsKey(casillaAnterior)){
            movsProhibidos.get(casillaAnterior).add(casillaProx);
        } else {
            List<String> array = new ArrayList<String>();
            array.add(casillaProx);
            movsProhibidos.put(casillaAnterior, array);
        }
    }
    
    private void iniciarLog(String metodo) {
        log.limpiar();
        log.setNombreFichero(metodo, this.getClass().getSimpleName());
        log.escribirLinea("-----------------------------------------------------------------");
        log.escribirLinea("-----------------------------------------------------------------");
        log.escribirLinea("INICIO DE LA BÚSQUEDA");
        log.escribirLinea("Metodo: " + metodo);
        log.escribirLinea("Heurística: " + this.getClass().getSimpleName());
        log.escribirLinea("");
        log.escribirLinea("");
        
    }
    
}
